<!DOCTYPE html>
<html CN>







<head>
	
	
	<link rel="stylesheet" href="/css/allinone.min.css"> 

	
	<!-- Global Site Tag (gtag.js) - Google Analytics -->
	<script async src="https://www.googletagmanager.com/gtag/js?id=UA-42863699-1"></script>
	<script>
		window.dataLayer = window.dataLayer || [];
		function gtag(){dataLayer.push(arguments);}
		gtag('js', new Date());
		gtag('config', 'UA-42863699-1');
	</script>
	

	<meta charset="utf-8" />
	<meta http-equiv="X-UA-Compatible" content="IE=edge" />

	<title>etcd go 语言 v2 客户端开发介绍 | Cizixs Write Here</title>

	<meta name="HandheldFriendly" content="True" />
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
	<meta name="generator" content="hexo">
	<meta name="author" content="Cizixs Wu">
	<meta name="description" content="">

	
	<meta name="keywords" content="">
	

	
	<link rel="shortcut icon" href="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg">
	

	
	<meta name="theme-color" content="#3c484e">
	<meta name="msapplication-TileColor" content="#3c484e">
	

	

	

	<meta property="og:site_name" content="Cizixs Write Here">
	<meta property="og:type" content="article">
	<meta property="og:title" content="etcd go 语言 v2 客户端开发介绍 | Cizixs Write Here">
	<meta property="og:description" content="">
	<meta property="og:url" content="http://cizixs.com/2017/12/03/etcd-v2-go-client/">

	
	<meta property="article:published_time" content="2017-12-03T00:12:00+08:00"/> 
	<meta property="article:author" content="Cizixs Wu">
	<meta property="article:published_first" content="Cizixs Write Here, /2017/12/03/etcd-v2-go-client/" />
	

	
	
	<script src="https://cdn.staticfile.org/jquery/3.2.1/jquery.min.js"></script>
	

	
	<script src="https://cdn.staticfile.org/highlight.js/9.10.0/highlight.min.js"></script>
	

	
	
<link rel="stylesheet" href="/css/prism-base16-ateliersulphurpool.light.css" type="text/css"></head>
<body class="post-template">
    <div class="site-wrapper">
        




<header class="site-header outer" style="z-index: 999">
    <div class="inner">
        
<nav class="site-nav"> 
    <div class="site-nav-left">
        <ul class="nav">
            <li>
                
                <a href="/" title="Home">Home</a>
                
            </li>
            
            
            <li>
                <a href="/about" title="About">About</a>
            </li>
            
            <li>
                <a href="/archives" title="Archives">Archives</a>
            </li>
            
            
        </ul> 
    </div>
    <div class="site-nav-right">
        
<div class="social-links" >
    
    <a class="social-link" title="weibo" href="https://weibo.com/1921727853" target="_blank" rel="noopener">
        <svg viewBox="0 0 1141 1024" xmlns="http://www.w3.org/2000/svg"><path d="M916.48 518.144q27.648 21.504 38.912 51.712t9.216 62.976-14.336 65.536-31.744 59.392q-34.816 48.128-78.848 81.92t-91.136 56.32-94.72 35.328-89.6 18.944-75.264 7.68-51.712 1.536-49.152-2.56-68.096-10.24-78.336-21.504-79.872-36.352-74.24-55.296-59.904-78.848q-16.384-29.696-22.016-63.488t-5.632-86.016q0-22.528 7.68-51.2t27.136-63.488 53.248-75.776 86.016-90.112q51.2-48.128 105.984-85.504t117.248-57.856q28.672-10.24 63.488-11.264t57.344 11.264q10.24 11.264 19.456 23.04t12.288 29.184q3.072 14.336 0.512 27.648t-5.632 26.624-5.12 25.6 2.048 22.528q17.408 2.048 33.792-1.536t31.744-9.216 31.232-11.776 33.28-9.216q27.648-5.12 54.784-4.608t49.152 7.68 36.352 22.016 17.408 38.4q2.048 14.336-2.048 26.624t-8.704 23.04-7.168 22.016 1.536 23.552q3.072 7.168 14.848 13.312t27.136 12.288 32.256 13.312 29.184 16.384zM658.432 836.608q26.624-16.384 53.76-45.056t44.032-64 18.944-75.776-20.48-81.408q-19.456-33.792-47.616-57.344t-62.976-37.376-74.24-19.968-80.384-6.144q-78.848 0-139.776 16.384t-105.472 43.008-72.192 60.416-38.912 68.608q-11.264 33.792-6.656 67.072t20.992 62.976 42.496 53.248 57.856 37.888q58.368 25.6 119.296 32.256t116.224 0.512 100.864-21.504 74.24-33.792zM524.288 513.024q20.48 8.192 38.912 18.432t32.768 27.648q10.24 12.288 17.92 30.72t10.752 39.424 1.536 42.496-9.728 38.912q-8.192 18.432-19.968 37.376t-28.672 35.328-40.448 29.184-57.344 18.944q-61.44 11.264-117.76-11.264t-88.064-74.752q-12.288-39.936-13.312-70.656t16.384-66.56q13.312-27.648 40.448-51.712t62.464-38.912 75.264-17.408 78.848 12.8zM361.472 764.928q37.888 3.072 57.856-18.432t21.504-48.128-15.36-47.616-52.736-16.896q-27.648 3.072-43.008 23.552t-17.408 43.52 9.728 42.496 39.424 21.504zM780.288 6.144q74.752 0 139.776 19.968t113.664 57.856 76.288 92.16 27.648 122.88q0 33.792-16.384 50.688t-35.328 17.408-35.328-14.336-16.384-45.568q0-40.96-22.528-77.824t-59.392-64.512-84.48-43.52-96.768-15.872q-31.744 0-47.104-15.36t-14.336-34.304 18.944-34.304 51.712-15.36zM780.288 169.984q95.232 0 144.384 48.64t49.152 146.944q0 30.72-10.24 43.52t-22.528 11.264-22.528-14.848-10.24-35.84q0-60.416-34.816-96.256t-93.184-35.84q-19.456 0-28.672-10.752t-9.216-23.04 9.728-23.04 28.16-10.752z" /></svg>
    </a>
    

    
    <a class="social-link" title="github" href="https://github.com/cizixs" target="_blank" rel="noopener">
        <svg viewBox="0 0 1049 1024" xmlns="http://www.w3.org/2000/svg"><path d="M524.979332 0C234.676191 0 0 234.676191 0 524.979332c0 232.068678 150.366597 428.501342 358.967656 498.035028 26.075132 5.215026 35.636014-11.299224 35.636014-25.205961 0-12.168395-0.869171-53.888607-0.869171-97.347161-146.020741 31.290159-176.441729-62.580318-176.441729-62.580318-23.467619-60.841976-58.234462-76.487055-58.234463-76.487055-47.804409-32.15933 3.476684-32.15933 3.476685-32.15933 53.019436 3.476684 80.83291 53.888607 80.83291 53.888607 46.935238 79.963739 122.553122 57.365291 152.97411 43.458554 4.345855-33.897672 18.252593-57.365291 33.028501-70.402857-116.468925-12.168395-239.022047-57.365291-239.022047-259.012982 0-57.365291 20.860106-104.300529 53.888607-140.805715-5.215026-13.037566-23.467619-66.926173 5.215027-139.067372 0 0 44.327725-13.906737 144.282399 53.888607 41.720212-11.299224 86.917108-17.383422 131.244833-17.383422s89.524621 6.084198 131.244833 17.383422C756.178839 203.386032 800.506564 217.29277 800.506564 217.29277c28.682646 72.1412 10.430053 126.029806 5.215026 139.067372 33.897672 36.505185 53.888607 83.440424 53.888607 140.805715 0 201.64769-122.553122 245.975415-239.891218 259.012982 19.121764 16.514251 35.636014 47.804409 35.636015 97.347161 0 70.402857-0.869171 126.898978-0.869172 144.282399 0 13.906737 9.560882 30.420988 35.636015 25.205961 208.601059-69.533686 358.967656-265.96635 358.967655-498.035028C1049.958663 234.676191 814.413301 0 524.979332 0z" /></svg>
    </a>
    

    
    <a class="social-link" title="stackoverflow" href="https://stackoverflow.com/users/1925083/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M15 21h-10v-2h10v2zm6-11.665l-1.621-9.335-1.993.346 1.62 9.335 1.994-.346zm-5.964 6.937l-9.746-.975-.186 2.016 9.755.879.177-1.92zm.538-2.587l-9.276-2.608-.526 1.954 9.306 2.5.496-1.846zm1.204-2.413l-8.297-4.864-1.029 1.743 8.298 4.865 1.028-1.744zm1.866-1.467l-5.339-7.829-1.672 1.14 5.339 7.829 1.672-1.14zm-2.644 4.195v8h-12v-8h-2v10h16v-10h-2z"/></svg>
    </a>
    

    

    
    <a class="social-link" title="twitter" href="https://twitter.com/cizixs" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><path d="M30.063 7.313c-.813 1.125-1.75 2.125-2.875 2.938v.75c0 1.563-.188 3.125-.688 4.625a15.088 15.088 0 0 1-2.063 4.438c-.875 1.438-2 2.688-3.25 3.813a15.015 15.015 0 0 1-4.625 2.563c-1.813.688-3.75 1-5.75 1-3.25 0-6.188-.875-8.875-2.625.438.063.875.125 1.375.125 2.688 0 5.063-.875 7.188-2.5-1.25 0-2.375-.375-3.375-1.125s-1.688-1.688-2.063-2.875c.438.063.813.125 1.125.125.5 0 1-.063 1.5-.25-1.313-.25-2.438-.938-3.313-1.938a5.673 5.673 0 0 1-1.313-3.688v-.063c.813.438 1.688.688 2.625.688a5.228 5.228 0 0 1-1.875-2c-.5-.875-.688-1.813-.688-2.75 0-1.063.25-2.063.75-2.938 1.438 1.75 3.188 3.188 5.25 4.25s4.313 1.688 6.688 1.813a5.579 5.579 0 0 1 1.5-5.438c1.125-1.125 2.5-1.688 4.125-1.688s3.063.625 4.188 1.813a11.48 11.48 0 0 0 3.688-1.375c-.438 1.375-1.313 2.438-2.563 3.188 1.125-.125 2.188-.438 3.313-.875z"/></svg>

    </a>
    

    
    <a class="social-link" title="instagram" href="https://www.instagram.com/cizixs/" target="_blank" rel="noopener">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M12 2.163c3.204 0 3.584.012 4.85.07 3.252.148 4.771 1.691 4.919 4.919.058 1.265.069 1.645.069 4.849 0 3.205-.012 3.584-.069 4.849-.149 3.225-1.664 4.771-4.919 4.919-1.266.058-1.644.07-4.85.07-3.204 0-3.584-.012-4.849-.07-3.26-.149-4.771-1.699-4.919-4.92-.058-1.265-.07-1.644-.07-4.849 0-3.204.013-3.583.07-4.849.149-3.227 1.664-4.771 4.919-4.919 1.266-.057 1.645-.069 4.849-.069zm0-2.163c-3.259 0-3.667.014-4.947.072-4.358.2-6.78 2.618-6.98 6.98-.059 1.281-.073 1.689-.073 4.948 0 3.259.014 3.668.072 4.948.2 4.358 2.618 6.78 6.98 6.98 1.281.058 1.689.072 4.948.072 3.259 0 3.668-.014 4.948-.072 4.354-.2 6.782-2.618 6.979-6.98.059-1.28.073-1.689.073-4.948 0-3.259-.014-3.667-.072-4.947-.196-4.354-2.617-6.78-6.979-6.98-1.281-.059-1.69-.073-4.949-.073zm0 5.838c-3.403 0-6.162 2.759-6.162 6.162s2.759 6.163 6.162 6.163 6.162-2.759 6.162-6.163c0-3.403-2.759-6.162-6.162-6.162zm0 10.162c-2.209 0-4-1.79-4-4 0-2.209 1.791-4 4-4s4 1.791 4 4c0 2.21-1.791 4-4 4zm6.406-11.845c-.796 0-1.441.645-1.441 1.44s.645 1.44 1.441 1.44c.795 0 1.439-.645 1.439-1.44s-.644-1.44-1.439-1.44z"/></svg>
    </a>
    
    
    
</div>
    </div>
</nav>
    </div>
</header>


<main id="site-main" class="site-main outer" role="main">
    <div class="inner">
        <header class="post-full-header">
            <section class="post-full-meta">
                <time  class="post-full-meta-date" datetime="2017-12-02T16:00:00.000Z" itemprop="datePublished">
                    2017-12-3
                </time>
                
                <span class="date-divider">/</span>
                
                <a href="/categories/blog/">blog</a>&nbsp;&nbsp;
                
                
            </section>
            <h1 class="post-full-title">etcd go 语言 v2 客户端开发介绍</h1>
        </header>
        <article class="post-full no-image">
            
            <section class="post-full-content">
                <div id="lightgallery" class="markdown-body">
                    <h2 id="etcd-介绍"><a href="#etcd-介绍" class="headerlink" title="etcd 介绍"></a>etcd 介绍</h2><p>很久之前写过<a href="http://cizixs.com/2016/08/02/intro-to-etcd">一篇 etcd 的介绍文章</a>，主要是讲 etcd 的概念和使用方式，这篇文章介绍如何使用 go 语言进行 etcd 的开发工作。</p>
<p>etcd 目前最新版 API 是 v3 版本，之前被广泛使用的 API 为 v2 版本。这篇文章会介绍 v2 版本 go 语言客户端的使用，涉及了最常见的增删查改和监听操作。</p>
<h2 id="v2-版本使用"><a href="#v2-版本使用" class="headerlink" title="v2 版本使用"></a>v2 版本使用</h2><p><a href="http://cizixs.com/2016/08/02/intro-to-etcd">之前的文章</a> 已经对 etcd v2 版本的概念和 API 介绍得很详细了，这里再总结一下。</p>
<ul>
<li>作为分布式的键值存储，etcd v2 存储的 key 和 value 都是字符串类型。其中 key 以类 unix 文件系统的结构存储，分为目录和文件，目录下面可以嵌套目录和文件，文件只能对应某个值（value）。可以对 key 进行创建、删除和修改的工作</li>
<li>可以为 key（包括目录和文件）设置一个 TTL 值，也就是过期时间，等到时间过了，值自动被删除</li>
<li>提供了 CAS(compare And Set）和 CAD（Compare And Delete）两种原子操作，可以实现比较然后执行某个动作的逻辑</li>
<li>可以监听某个文件和目录，当里面的值发生变化时，监听者会立即收到通知</li>
</ul>
<p>概念讲完了，下面看看如何用 go 语言进行 etcd 的开发工作。etcd 官方就维护了一个 etcd go 语言客户端，在 <code>github.com/coreos/etcd/client</code>，我们可以用下面的语句把它导入到 go 程序中：</p>
<pre><code>import    (
    ...
    etcd &quot;github.com/coreos/etcd/client&quot;
    &quot;github.com/coreos/etcd/pkg/transport&quot;
)
</code></pre><p>我们给它取了个别名 <code>etcd</code>，之后后面的程序中我们可以用 <code>etcd</code> 而不是 <code>client</code> 来使用它提供的功能（这样程序的可读性更好）。</p>
<h3 id="创建-client-客户端"><a href="#创建-client-客户端" class="headerlink" title="创建 client 客户端"></a>创建 client 客户端</h3><p>首先，我们要创建一个 etcd 的 Client，可以使用 <code>etcd.New()</code> 函数，它接受 <code>etcd.Config</code> 作为参数，后者主要的参数包括：</p>
<ul>
<li><code>Endpoints</code>：etcd server 集群的地址列表，是一个 string 列表，每个值为一个 etcd 的监听地址</li>
<li><code>Transport</code>：底层用于进行 HTTP 请求传输的结构体</li>
<li><code>Username</code> 和 <code>Password</code>：进行简单认证的用户名和密码</li>
</ul>
<p>其他还有一些参数这里就不说了，使用默认值就行。为了完整性，我们会处理 etcd server 开启了简单认证和 TLS 的情况，首先创建一个自定义的结构体保存所有需要的参数：</p>
<pre><code>type EtcdConfig struct {
    Endpoints []string
    KeyFile   string
    CertFile  string
    CAFile    string
    Username  string
    Password  string
}
</code></pre><p>注意这是我们自己定义的结构体，不是客户端程序提供的。</p>
<p>然后构建能进行 TLS 处理的 transport，<code>transport</code> 是 etcd 提供的库，它内部会处理证书的读取和验证工作：</p>
<pre><code>tlsInfo := transport.TLSInfo{
    CertFile: c.CertFile,
    KeyFile:  c.KeyFile,
    CAFile:   c.CAFile,
}

t, err := transport.NewTransport(tlsInfo, time.Second)
</code></pre><p>然后就能构建 client：</p>
<pre><code>client, err := etcd.New(etcd.Config{
    Endpoints: c.Endpoints,
    Transport: t,
    Username:  c.Username,
    Password:  c.Password,
})
</code></pre><p>这个 client 主要负责 HTTP API 交互的逻辑，同时也负责从多个 endpoints 中选择一个可用的来调用。但是和 keys 交互的逻辑并没有直接在这个 client 中，而是需要另外一个对象 <code>KeysAPI</code>：</p>
<pre><code>kapi := etcd.NewKeysAPI(client)
</code></pre><p>所有键值对有关的操作都是这个 <code>kapi</code> 对象的方法提供的，它会实现接口的如下方法：</p>
<pre><code>type KeysAPI interface {
    // Get 从 etcd 中获取 Node 的信息
    Get(ctx context.Context, key string, opts *GetOptions) (*Response, error)

    // Set 设置对应 key 的值为 value，这个方法也可以用来创建目录
    Set(ctx context.Context, key, value string, opts *SetOptions) (*Response, error)

    // Delete 删除节点，包括目录和文件节点
    Delete(ctx context.Context, key string, opts *DeleteOptions) (*Response, error)

    // Create 是 set 的一种特殊形式，只有之前节点不存在时才会创建成功
    Create(ctx context.Context, key, value string) (*Response, error)

    // CreateInOrder 在目录下面创建递增的键值对
    CreateInOrder(ctx context.Context, dir, value string, opts *CreateInOrderOptions) (*Response, error)

    // Update 也是 Set 的一种特殊形式，只有对应的节点存在时才会更新成功
    Update(ctx context.Context, key, value string) (*Response, error)

    // Watcher 返回一个 Watcher 对象，监听某个节点下面的变化
    Watcher(key string, opts *WatcherOptions) Watcher
}
</code></pre><h3 id="创建值"><a href="#创建值" class="headerlink" title="创建值"></a>创建值</h3><p>先来看看设置一个 key value 的操作，第一个是 context 对象，后面两个分别是 key 和 value 值，最后是额外的参数，目前并不需要设置为 nil：</p>
<pre><code>resp, err := client.Set(context.Background(), &quot;/user/name&quot;, &quot;cizixs&quot;, nil)
</code></pre><p>如果要为某个值设置超时时间，可以添加选项 <code>TTL</code>：</p>
<pre><code>resp, err := client.Set(context.Background(), &quot;/user/name&quot;, &quot;&quot;,
    &amp;etcd.SetOptions{
        TTL: time.Duration(ttl) * time.Second,
        Refresh: true,
    },
)
</code></pre><p><code>SetOptions</code> 可选的字段有：</p>
<ul>
<li><code>Dir</code>：布尔值，表示要创建的是一个目录</li>
<li><code>Refresh</code>：如果设置为 true，则表明只是更新某个 key 的 TTL 时间，不会重置它的值</li>
<li><code>PrevValue</code>：原子操作，只有节点的值和这个字段指定的值相同时才会执行更新操作</li>
<li><code>PrevIndex</code>：原子操作，只有节点的 ModifiedIndex 和这个字段指定的值相同时才会执行更新操作</li>
<li><code>PrevExist</code>：原子操作，只有节点存在或者不存在时才会执行更新操作，支持的值有 <code>PrevIgnore</code>、<code>PrevExist</code> 和 <code>PrevNoExist</code></li>
</ul>
<p>这里要先讲一下返回值 <code>Response</code> 的构成，它包含了返回中节点的信息以及 index 信息：</p>
<pre><code>type Response struct {
    // Action 操作的动作名称，可以是 set、delete
    Action string `json:&quot;action&quot;`

    // Node 代表操作的节点
    Node *Node `json:&quot;node&quot;`

    // PrevNode 节点之前的值
    PrevNode *Node `json:&quot;prevNode&quot;`

    // Index：response 生成是 cluster index 的值
    Index uint64 `json:&quot;-&quot;`
}
</code></pre><p>其中节点的定义如下：</p>
<pre><code>type Node struct {
    //  节点的位置，目录名称，比如 `/foo/bar`
    Key string `json:&quot;key&quot;`

    //  节点是否为一个目录
    Dir bool `json:&quot;dir,omitempty&quot;`

    //  节点存储的对象值，如果是目录，则忽略这个字段
    Value string `json:&quot;value&quot;`

    // Nodes 子节点，如果是目录，则这个字段保存了目录下面的所有节点内容
    Nodes Nodes `json:&quot;nodes&quot;`

    //  节点创建时候的 etcd  index
    CreatedIndex uint64 `json:&quot;createdIndex&quot;`

    //  节点更新时候的 etcd index
    ModifiedIndex uint64 `json:&quot;modifiedIndex&quot;`

    // 节点的过期时间
    Expiration *time.Time `json:&quot;expiration,omitempty&quot;`

    // TTL 节点设置的 time to live，单位是秒
    TTL int64 `json:&quot;ttl,omitempty&quot;`
}
</code></pre><h3 id="获取值"><a href="#获取值" class="headerlink" title="获取值"></a>获取值</h3><p>获取值是通过 <code>Get</code> 方法，和 <code>Set</code> 一样，它也支持选项。如果只是简单获取某个键的值，直接设置选项为空就行：</p>
<pre><code>resp, err := client.Get(context.Background(), key, nil)
</code></pre><p>如果要递归地获取某个目录下面所有的内容，可以使用 <code>Recursive</code> 参数：</p>
<pre><code>resp, err := client.Get(context.Background(), key, &amp;etcd.GetOptions{
    Recursive: true,   
})
</code></pre><p>然后通过 resp 就能遍历所有的子节点的值。</p>
<h3 id="删除值"><a href="#删除值" class="headerlink" title="删除值"></a>删除值</h3><p>删除操作通过 <code>Delete</code> 方法完成，简单删除一个文件节点，可以忽略参数：</p>
<pre><code>_, err := client.Delete(context.Background(), key, nil)
</code></pre><p>如果要删除某个非空的目录，需要 <code>Recursive</code> 参数：</p>
<pre><code>_, err := client.Delete(context.Background(), key, &amp;etcd.DeleteOptions{
    Dir: true,
    Recursive: true,
})
</code></pre><p>其他字段包括：</p>
<ul>
<li><code>PrevValue</code>：只有节点值和给定的值相同时才执行删除操作</li>
<li><code>PrevIndex</code>：只有节点的 index 和给定的 index 相同时，才执行删除操作</li>
</ul>
<h3 id="监听值"><a href="#监听值" class="headerlink" title="监听值"></a>监听值</h3><p>etcd 另外一个重要的功能是监听目录或者文件的变化，<code>Watcher</code> 方法会返回一个对象，调用它的 <code>Next()</code> 方法会阻塞，一直到监听的对象有变化，它才会返回变化节点的情况：</p>
<pre><code>watcher := client.Watcher(key, &amp;etcd.WatcherOptions{
    Recursive: true,
})

for {
    resp, err := watcher.Next(context.Background())
    if err != nil {
        return err
    }
}
</code></pre><p>其中 <code>WatcherOptions</code> 接受 <code>AfterIndex</code> 和 <code>Recursive</code> 两个参数，分别表示才某个 Index 之后开始监听，以及递归监听某个目录下面所有的节点。</p>
<p>完整的 demo 代码在<a href="https://github.com/cizixs/etcd-demo/tree/master/v2" target="_blank" rel="noopener"> github 上</a>，请前往阅读。</p>
<h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul>
<li><a href="https://godoc.org/github.com/coreos/etcd/client" target="_blank" rel="noopener">etcd client v2 godoc 文档</a></li>
<li><a href="https://github.com/coreos/etcd/tree/master/client" target="_blank" rel="noopener">etcd client 源码</a></li>
<li><a href="http://dockone.io/article/801" target="_blank" rel="noopener">谈谈CoreOS的etcd</a></li>
</ul>

                </div>
            </section>
        </article>
    </div>
    
<nav class="pagination">
    
    
    <a class="prev-post" title="raft 一致性算法" href="/2017/12/04/raft-consensus-algorithm/">
        ← raft 一致性算法
    </a>
    
    <span class="prev-next-post">•</span>
    
    <a class="next-post" title="OCI 和 runc：容器标准化和 docker" href="/2017/11/05/oci-and-runc/">
        OCI 和 runc：容器标准化和 docker →
    </a>
    
    
</nav>

    <div class="inner">
    <!-- Begin Mailchimp Signup Form -->
    <link href="//cdn-images.mailchimp.com/embedcode/classic-10_7.css" rel="stylesheet" type="text/css">
    <style type="text/css">
    	#mc_embed_signup{background:#fff; clear:left; font:14px Helvetica,Arial,sans-serif; }
    	/* Add your own Mailchimp form style overrides in your site stylesheet or in this style block.
    	   We recommend moving this block and the preceding CSS link to the HEAD of your HTML file. */
    </style>
    <div id="mc_embed_signup">
    <form action="https://cizixs.us7.list-manage.com/subscribe/post?u=2d561b8dea52d73a2e05e6dcb&amp;id=5c710f135b" method="post" id="mc-embedded-subscribe-form" name="mc-embedded-subscribe-form" class="validate" target="_blank" novalidate>
        <div id="mc_embed_signup_scroll">
    	<h2>订阅本博客，第一时间收到文章更新</h2>
    <div class="indicates-required"><span class="asterisk">*</span> indicates required</div>
    <div class="mc-field-group">
    	<label for="mce-EMAIL">邮件地址  <span class="asterisk">*</span>
    </label>
    	<input type="email" value="" name="EMAIL" class="required email" id="mce-EMAIL">
    </div>
    	<div id="mce-responses" class="clear">
    		<div class="response" id="mce-error-response" style="display:none"></div>
    		<div class="response" id="mce-success-response" style="display:none"></div>
    	</div>    <!-- real people should not fill this in and expect good things - do not remove this or risk form bot signups-->
        <div style="position: absolute; left: -5000px;" aria-hidden="true"><input type="text" name="b_2d561b8dea52d73a2e05e6dcb_5c710f135b" tabindex="-1" value=""></div>
        <div class="clear"><input type="submit" value="Subscribe" name="subscribe" id="mc-embedded-subscribe" class="button"></div>
        </div>
    </form>
    </div>
    <script type='text/javascript' src='//s3.amazonaws.com/downloads.mailchimp.com/js/mc-validate.js'></script><script type='text/javascript'>(function($) {window.fnames = new Array(); window.ftypes = new Array();fnames[0]='EMAIL';ftypes[0]='email';}(jQuery));var $mcj = jQuery.noConflict(true);</script>
    <!--End mc_embed_signup-->
    </div>

    <div class="inner">
        <div id="disqus_thread"></div>
    </div>

    
</main>

<div class="t-g-control">
    <div class="gotop">
        <svg class="icon" width="32px" height="32px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M793.024 710.272a32 32 0 1 0 45.952-44.544l-310.304-320a32 32 0 0 0-46.4 0.48l-297.696 320a32 32 0 0 0 46.848 43.584l274.752-295.328 286.848 295.808z" fill="#8a8a8a" /></svg>
    </div>
    <div class="toc-control">
        <svg class="icon toc-icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M779.776 480h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M779.776 672h-387.2a32 32 0 0 0 0 64h387.2a32 32 0 0 0 0-64M256 288a32 32 0 1 0 0 64 32 32 0 0 0 0-64M392.576 352h387.2a32 32 0 0 0 0-64h-387.2a32 32 0 0 0 0 64M256 480a32 32 0 1 0 0 64 32 32 0 0 0 0-64M256 672a32 32 0 1 0 0 64 32 32 0 0 0 0-64" fill="#8a8a8a" /></svg>
        <svg class="icon toc-close" style="display: none;" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M512 960c-247.039484 0-448-200.960516-448-448S264.960516 64 512 64 960 264.960516 960 512 759.039484 960 512 960zM512 128.287273c-211.584464 0-383.712727 172.128262-383.712727 383.712727 0 211.551781 172.128262 383.712727 383.712727 383.712727 211.551781 0 383.712727-172.159226 383.712727-383.712727C895.712727 300.415536 723.551781 128.287273 512 128.287273z" fill="#8a8a8a" /><path d="M557.05545 513.376159l138.367639-136.864185c12.576374-12.416396 12.672705-32.671738 0.25631-45.248112s-32.704421-12.672705-45.248112-0.25631l-138.560301 137.024163-136.447897-136.864185c-12.512727-12.512727-32.735385-12.576374-45.248112-0.063647-12.512727 12.480043-12.54369 32.735385-0.063647 45.248112l136.255235 136.671523-137.376804 135.904314c-12.576374 12.447359-12.672705 32.671738-0.25631 45.248112 6.271845 6.335493 14.496116 9.504099 22.751351 9.504099 8.12794 0 16.25588-3.103239 22.496761-9.247789l137.567746-136.064292 138.687596 139.136568c6.240882 6.271845 14.432469 9.407768 22.65674 9.407768 8.191587 0 16.352211-3.135923 22.591372-9.34412 12.512727-12.480043 12.54369-32.704421 0.063647-45.248112L557.05545 513.376159z" fill="#8a8a8a" /></svg>
    </div>
    <div class="gobottom">
        <svg class="icon" width="32px" height="32.00px" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"><path d="M231.424 346.208a32 32 0 0 0-46.848 43.584l297.696 320a32 32 0 0 0 46.4 0.48l310.304-320a32 32 0 1 0-45.952-44.544l-286.848 295.808-274.752-295.36z" fill="#8a8a8a" /></svg>
    </div>
</div>
<div class="toc-main" style="right: -100%">
    <div class="post-toc">
        <span>TOC</span>
        <ol class="toc"><li class="toc-item toc-level-2"><a class="toc-link" href="#etcd-介绍"><span class="toc-text">etcd 介绍</span></a></li><li class="toc-item toc-level-2"><a class="toc-link" href="#v2-版本使用"><span class="toc-text">v2 版本使用</span></a><ol class="toc-child"><li class="toc-item toc-level-3"><a class="toc-link" href="#创建-client-客户端"><span class="toc-text">创建 client 客户端</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#创建值"><span class="toc-text">创建值</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#获取值"><span class="toc-text">获取值</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#删除值"><span class="toc-text">删除值</span></a></li><li class="toc-item toc-level-3"><a class="toc-link" href="#监听值"><span class="toc-text">监听值</span></a></li></ol></li><li class="toc-item toc-level-2"><a class="toc-link" href="#参考资料"><span class="toc-text">参考资料</span></a></li></ol>
    </div>
</div>



        

<aside class="read-next outer">
    <div class="inner">
        <div class="read-next-feed">
            
            

<article class="read-next-card"  style="background-image: url(https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxcn9ft3j318w0txdo6.jpg)"  >
  <header class="read-next-card-header">
    <small class="read-next-card-header-sitetitle">&mdash; Cizixs Write Here &mdash;</small>
    <h3 class="read-next-card-header-title">Recent Posts</h3>
  </header>
  <div class="read-next-divider">
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
      <path d="M13 14.5s2 3 5 3 5.5-2.463 5.5-5.5S21 6.5 18 6.5c-5 0-7 11-12 11C2.962 17.5.5 15.037.5 12S3 6.5 6 6.5s4.5 3.5 4.5 3.5"/>
    </svg>
  </div>
  <div class="read-next-card-content">
    <ul>
      
      
      
      <li>
        <a href="/2018/08/26/what-is-istio/">什么是 istio</a>
      </li>
      
      
      
      <li>
        <a href="/2018/08/25/knative-serverless-platform/">serverless 平台 knative 简介</a>
      </li>
      
      
      
      <li>
        <a href="/2018/06/25/kubernetes-resource-management/">kubernetes 资源管理概述</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/24/use-prometheus-and-grafana-to-monitor-linux-machine/">使用 promethues 和 grafana 监控自己的 linux 机器</a>
      </li>
      
      
      
      <li>
        <a href="/2018/01/13/linux-udp-packet-drop-debug/">linux 系统 UDP 丢包问题分析思路</a>
      </li>
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    </ul>
  </div>
  <footer class="read-next-card-footer">
    <a href="/archives">  MORE  → </a>
  </footer>
</article>


            
            
            
        </div>
    </div>
</aside>


<footer class="site-footer outer">

	<div class="site-footer-content inner">
		<section class="copyright">
			<a href="/" title="Cizixs Write Here">Cizixs Write Here</a>
			&copy; 2019
		</section>
		<nav class="site-footer-nav">
			
            <a href="https://hexo.io" title="Hexo" target="_blank" rel="noopener">Hexo</a>
            <a href="https://github.com/xzhih/hexo-theme-casper" title="Casper" target="_blank" rel="noopener">Casper</a>
        </nav>
    </div>
</footer>






<div class="floating-header" >
	<div class="floating-header-logo">
        <a href="/" title="Cizixs Write Here">
			
                <img src="https://cizixs-blog.oss-cn-beijing.aliyuncs.com/006tNc79ly1g1qxfovpzyj30740743yg.jpg" alt="Cizixs Write Here icon" />
			
            <span>Cizixs Write Here</span>
        </a>
    </div>
    <span class="floating-header-divider">&mdash;</span>
    <div class="floating-header-title">etcd go 语言 v2 客户端开发介绍</div>
    <progress class="progress" value="0">
        <div class="progress-container">
            <span class="progress-bar"></span>
        </div>
    </progress>
</div>
<script>
   $(document).ready(function () {
    var progressBar = document.querySelector('progress');
    var header = document.querySelector('.floating-header');
    var title = document.querySelector('.post-full-title');
    var lastScrollY = window.scrollY;
    var lastWindowHeight = window.innerHeight;
    var lastDocumentHeight = $(document).height();
    var ticking = false;

    function onScroll() {
        lastScrollY = window.scrollY;
        requestTick();
    }
    function requestTick() {
        if (!ticking) {
            requestAnimationFrame(update);
        }
        ticking = true;
    }
    function update() {
        var rect = title.getBoundingClientRect();
        var trigger = rect.top + window.scrollY;
        var triggerOffset = title.offsetHeight + 35;
        var progressMax = lastDocumentHeight - lastWindowHeight;
            // show/hide floating header
            if (lastScrollY >= trigger + triggerOffset) {
                header.classList.add('floating-active');
            } else {
                header.classList.remove('floating-active');
            }
            progressBar.setAttribute('max', progressMax);
            progressBar.setAttribute('value', lastScrollY);
            ticking = false;
        }

        window.addEventListener('scroll', onScroll, {passive: true});
        update();

        // TOC
        var width = $('.toc-main').width();
        $('.toc-control').click(function () {
            if ($('.t-g-control').css('width')=="50px") {
                if ($('.t-g-control').css('right')=="0px") {
                    $('.t-g-control').animate({right: width}, "slow");
                    $('.toc-main').animate({right: 0}, "slow");
                    toc_icon()
                } else {
                    $('.t-g-control').animate({right: 0}, "slow");
                    $('.toc-main').animate({right: -width}, "slow");
                    toc_icon()
                }
            } else {
                if ($('.toc-main').css('right')=="0px") {
                    $('.toc-main').slideToggle("fast", toc_icon());
                } else {
                    $('.toc-main').css('right', '0px');
                    toc_icon()
                }
            }
        })

        function toc_icon() {
            if ($('.toc-icon').css('display')=="none") {
                $('.toc-close').hide();
                $('.toc-icon').show();
            } else {
                $('.toc-icon').hide();
                $('.toc-close').show();
            }
        }

        $('.gotop').click(function(){
            $('html,body').animate({scrollTop:$('.post-full-header').offset().top}, 800);
        });
        $('.gobottom').click(function () {
            $('html,body').animate({scrollTop:$('.pagination').offset().top}, 800);
        });

        // highlight
        // https://highlightjs.org
        $('pre code').each(function(i, block) {
            hljs.highlightBlock(block);
        });
        $('td.code').each(function(i, block) {
            hljs.highlightBlock(block);
        });

        console.log("this theme is from https://github.com/xzhih/hexo-theme-casper")
    });
</script>



<link rel="stylesheet" href="https://cdn.staticfile.org/lightgallery/1.3.9/css/lightgallery.min.css">



<script src="https://cdn.staticfile.org/lightgallery/1.3.9/js/lightgallery.min.js"></script>


<script>
	$(function () {
		var postImg = $('#lightgallery').find('img');
		postImg.addClass('post-img');
		postImg.each(function () {
			var imgSrc = $(this).attr('src');
			$(this).attr('data-src', imgSrc);
		});
		$('#lightgallery').lightGallery({selector: '.post-img'});
	});
</script>



<script>

/**
*  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
*  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/

var disqus_config = function () {
this.page.url = 'http://cizixs.com/2017/12/03/etcd-v2-go-client/';  // Replace PAGE_URL with your page's canonical URL variable
this.page.identifier = 'http://cizixs.com/2017/12/03/etcd-v2-go-client/'; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
};

(function() { // DON'T EDIT BELOW THIS LINE
var d = document, s = d.createElement('script');
s.src = 'https://cizixs.disqus.com/embed.js';
s.setAttribute('data-timestamp', +new Date());
(d.head || d.body).appendChild(s);
})();
</script>
<noscript>Please enable JavaScript to view the <a href="https://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
                            


    </div>
</body>
</html>
